# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.exceptions import ValidationError
from collections import OrderedDict, defaultdict
import xlrd
import logging
import base64
import datetime
import traceback

_logger = logging.getLogger(__name__)

DEFAULT_SERVER_DATE_FORMAT = "%Y-%m-%d"
DEFAULT_SERVER_TIME_FORMAT = "%H:%M:%S"
DEFAULT_SERVER_DATETIME_FORMAT = "%s %s" % (DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_TIME_FORMAT)


class ImportExcelWizard(models.AbstractModel):
    _name = 'import.excel.wizard'
    _description = '导入Excle文件'

    HEARDER = {}  # {0: '名称', 1: '说明'}
    HEARDER_ROW = 0  # 标题的行号
    COLUMN_MAP = {}  # 字段映射, 比如 {'name': '名称'}
    DATA = {}
    error_messages = OrderedDict({})

    data_file = fields.Binary(string='上传文件', help='请上传excel文件')
    filename = fields.Char(string='文件')
    step = fields.Selection(
        string='步骤',
        selection=[('1', '验证数据'), ('2', '导入数据'), ('3', '导入完毕'), ('4', '导入异常')],
        default='1'
    )
    error_info = fields.Html(string='异常信息')
    error_file = fields.Binary(string='异常信息文件', help='异常信息文件')
    error_filename = fields.Char(string='异常文件')

    @api.model
    def set_ecolno(self, header): #货期excel第一行（标题）的字典 {0: '名称', 1: '说明'}
        headers = {}
        for index, cell in enumerate(header):
            # 列名, 去掉回车和空格
            column = str(cell.value).replace('\n', '').replace(' ', '')
            headers.update({index: column})

        diff = set(self.COLUMN_MAP.keys()) - set(headers.values())
        if diff:
            msg = 'Excel文件中缺少列: %s.' % ','.join(diff)
            self.error_messages.setdefault(self.HEARDER_ROW, []).append(msg)

        self.HEARDER = headers

    @api.model
    def get_field_name(self, colno): #通过标题行的第几个，获取标题对应的字段
        colname = self.HEARDER.get(colno, '')
        if colname is None:
            msg = 'Excel文件中没有%s列.' % colname
            _logger.error(msg)
            raise ValidationError('文件不合规，请下载“导入模板”, 按模板要求上传:\n%s' % msg)

        return self.COLUMN_MAP.get(colname, '')

    def clear(self):
        self.HEARDER = {}
        self.error_messages = OrderedDict({})
        self.DATA = {}

    def _prepare_import_wizard(self, step):
        self.ensure_one()
        view_id = self.env.ref('mlupdate.view_import_excel_wizard')
        view_id = view_id and view_id.id or False

        return {
            'name': '导入数据',
            'type': 'ir.actions.act_window',
            'res_model': self._name,
            'views': [(view_id, 'form')],
            'view_id': view_id,
            'res_id': self.id,
            'target': 'new',
            'context': self._context,
        }

    def import_file(self):
        self.clear()
        if not self.data_file:
            raise ValidationError('文件未选择，请下载“导入模板”，按模板要求上传!')
        try:
            book = xlrd.open_workbook(file_contents=base64.b64decode(self.data_file))
            sheet = book.sheet_by_index(0)
            self.set_ecolno(sheet.row(self.HEARDER_ROW))

            data_list = []
            for rownum in range(self.HEARDER_ROW + 1, sheet.nrows):
                self.row_error_info = []
                format_data = self._format_excel_data(book, sheet.row(rownum)) #对某一数据行获得，某列的数据，产生（某行，{’字段‘：要导入的数据}）
                # self._validate_data(rownum, format_data)
                # data_list.append(
                #     (rownum, {
                #         self.get_field_name(cloname): value
                #         for cloname, value in format_data.items() if self.get_field_name(cloname)
                #     })
                # )
                data = {
                    self.get_field_name(cloname): value
                    for cloname, value in format_data.items() if self.get_field_name(cloname)
                }
                self._validate_data(rownum, data) #首先检测所有要导入的数据，如果有错误就存入excel文件退出，这时不会导入
                data_list.append((rownum, data))

                if self.row_error_info:
                    self.error_messages.setdefault(rownum, []).extend(self.row_error_info)

        except Exception as e:
            traceback.print_exc()
            self.error_messages.setdefault(0, []).append(str(e))

        # def join_msg(messages):
        #     msgs = []
        #     for row, items in messages.items():
        #         msg = '%s%s' % (
        #             ('<h3>第%d行</h3>' % (row + 1)) if row > 0 else '',
        #             '<ul>%s</ul>' % (''.join(['<li>%s</li>' % x for x in items]),)
        #         )
        #         msgs.append(msg)
        #     return ''.join(msgs)

        if self.error_messages:
            self.step = '4'
            # self.error_info = join_msg(self.error_messages)
            self._process_errors(self.error_messages)

        if self.step == '1': #验证完数据就设置step='2',(只要验证数据有问题，会在前面设置step='4')
            self.step = '2'
        elif self.step == '2':
            self.step = '3'

            try:
                self._before_process_row_data(book, sheet)
                # 处理每行数据
                for rownum, data in data_list:  #data_list=[（某行，{’字段‘：要导入的数据}）,...]
                    self._process_row_data(rownum, data) #在被导入model的方法中执行导入

                self._after_process_row_data(book, sheet) #导入完毕后，执行这个方法
            except Exception as e:
                self.step = '4'
                self.error_info = str(e) or '出现错误!'

        if self.error_info:
            _logger.error(self.error_info)

        return self._prepare_import_wizard(self.step) #执行action_win进入导入界面

    def _process_errors(self, error_messages):
        def join_msg(messages):
            msgs = []
            for row, items in messages.items():
                msg = '%s%s' % (
                    ('<h3>第%d行</h3>' % (row + 1)) if row > 0 else '',
                    '<ul>%s</ul>' % (''.join(['<li>%s</li>' % x for x in items]),)
                )
                msgs.append(msg)
            return ''.join(msgs)

        self.error_info = join_msg(error_messages)

    @api.model
    def _format_excel_data(self, book, row):
        values = {}
        for colno, cell in enumerate(row):
            if cell.ctype is xlrd.XL_CELL_DATE:
                is_datetime = cell.value % 1 != 0.0
                dt = datetime.datetime(*xlrd.xldate.xldate_as_tuple(cell.value, book.datemode))
                value = dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT) if is_datetime else dt.strftime(
                    DEFAULT_SERVER_DATE_FORMAT)
                values.update({colno: value})
            else:
                colname = self.HEARDER.get(colno, '')
                field_name = self.COLUMN_MAP.get(colname, '')
                value = self._format_data(field_name, cell.value)
                values.update({colno: value})
        return values

    def _validate_data(self, rownum, format_data):
        '''对每行数据进行验证'''
        pass

    def _format_data(self, field_name, value):
        return value

    def _before_process_row_data(self, book, sheet):
        pass

    def _process_row_data(self, rownum, data):
        '''对每行数据进行处理'''
        raise NotImplementedError('方法_process_row_data没有实现.')

    def _after_process_row_data(self, book, sheet):
        pass
